package com.gta.edu.sdk.util;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSetMetaData;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.sql.RowSet;

import com.gta.edu.sdk.base.dao.impl.BaseDaoImpl;
import com.gta.edu.sdk.common.dto.EntityTableMapDto;
import com.gta.edu.sdk.mybatis.common.Entity;

public class EntityTableReflectUtil {

	/**
	 * @description 根据给定的实体类的类型，得到该实体类对应的表中表名和字段信息
	 * @author fengya
	 * @date 2017年6月26日 上午11:56:12
	 * @param cls
	 * @return
	 * @return EntityTableMapDto
	 */
	public static EntityTableMapDto tableReflect(Class<?> cls) {
		Field[] fields = cls.getDeclaredFields();
		String entityName = cls.getSimpleName();
		String tableName = classNameToTable(entityName);
		EntityTableMapDto entityTableMapDto = new EntityTableMapDto();
		entityTableMapDto.setTableName(tableName);
		for (Field field : fields) {
			if (field.getModifiers() != 130) {
				entityTableMapDto.getFields().put(field.getName(), classNameToTable(field.getName()));
			}
		}
		return entityTableMapDto;
	}

	/**
	 * @description 根据实体类中的名称转化为表中对应的名称，前提：实体类的名称必须按照表中字段名进行驼峰规则定义
	 * @author fengya
	 * @date 2017年6月26日 上午11:57:53
	 * @param className
	 * @return
	 * @return String
	 */
	public static String classNameToTable(String className) {
		StringBuilder tableName = new StringBuilder();
		char[] chs = className.toCharArray();
		for (int i = 0; i < chs.length; i++) {
			if (Character.isUpperCase(chs[i]) && i != 0) {
				tableName.append('_').append(Character.toLowerCase(chs[i]));
			} else {
				tableName.append(Character.toLowerCase(chs[i]));
			}
		}
		return tableName.toString();
	}

	/**
	 * @description 根据实体类对象中的属性非空找出对应的sql语句的字段名和对应字段的值
	 * @author fengya
	 * @date 2017年6月26日 上午11:59:22
	 * @param entity
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @return Map<String,Object>
	 */
	 public static Map<String,Object> entitySQLWhere(Entity entity) throws IllegalArgumentException, IllegalAccessException {
		Field[] fields = entity.getClass().getDeclaredFields();
		Map<String,Object> whereValue = new HashMap<String,Object>();
		for (Field field : fields) {
			if (field.getModifiers() != 130) {
				field.setAccessible(true); //设置些属性是可以访问的
				Object val = field.get(entity);//得到此属性的值
				String type = field.getType().toString();//得到此属性的类型
				if (type.endsWith("String")) {
					if ("".equals(val)){
						continue;
					}
				}
				if (val != null) {
					whereValue.put(classNameToTable(field.getName()), val);
				}
			}
		}
		return whereValue;
	}

	/**
	 * @description 根据实体类和数据集把数据集中的数据赋值给实体类生成的对象，对应的属性值上。
	 * @author fengya
	 * @date 2017年6月26日 下午1:15:52
	 * @param cls 实体类类型
	 * @param rs 数据集
	 * @return
	 * @throws Exception
	 * @return T
	 */
//	public static <T> T getObject(Class<T> cls, RowSet rs) throws Exception {
	public static <T> T getObject(Class<T> cls, RowSet rs) {
		T instance = null;
		Class<?> columnClassType = null;
		String columnTypeName = null;
		String columnName = null;
		Object value = null;
		try{
		// 要确定该类有无参的构造方法
		instance = cls.newInstance();
		// 获取元数据，以便得到列的信息
		ResultSetMetaData metaData = rs.getMetaData();
		// 获取列数
		int columnNum = metaData.getColumnCount();
		for (int i = 1; i <= columnNum; i++) {
			columnName = getColumnName(metaData.getColumnLabel(i));
			if (columnName.toLowerCase().equals(BaseDaoImpl.ROWNUM_NAME)) {
				continue;
			}
			columnClassType = SQLTypeToClass(metaData.getColumnType(i));
			// columnClassType = getColumnClassType(columnName, columnClassType,
			// cls);
			columnTypeName = columnTypeToGetter(metaData.getColumnTypeName(i));
			if (columnTypeName.equals("int")) {
				Field field = cls.getDeclaredField(columnName.substring(0, 1).toLowerCase() + columnName.substring(1));
				field.setAccessible(true);
				columnTypeName = getNumberTypeName(field.getType().getName());
				columnClassType = field.getType();
			}
			// 反射获取对象的set方法
			Method objectMethod = cls.getMethod("set" + columnName, columnClassType);

			// 反射获取CachedRowSetImpl的get方法
			Method RSGetter = com.sun.rowset.CachedRowSetImpl.class.getMethod("get" + columnTypeName.substring(0, 1).toUpperCase() + columnTypeName.substring(1), int.class);
			// 执行RS的get方法获取属性值
			if (columnClassType.getTypeName().equals("byte[]")) {
				InputStream is = rs.getBinaryStream(i);
				if (is != null) {
					byte[] blogValue = new byte[is.available()];
					is.read(blogValue);
					objectMethod.invoke(instance, blogValue);
				}
			} else {
				value = RSGetter.invoke(rs, i);
				if (columnClassType.getName().equals("char")) {
					value = value.toString().charAt(0);
				}
				if (columnClassType.getName().equals("java.util.Date")) {
					if (value != null) {
//						if (value instanceof java.sql.Timestamp) {
//							value = new Date(((Timestamp)value).getTime());
//						} else if (value instanceof oracle.sql.TIMESTAMP){
//							value = ((oracle.sql.TIMESTAMP)value).dateValue();
//						}
						if (value instanceof java.sql.Timestamp) {
							value = new Date(((Timestamp)value).getTime());
						}
					}
				}
				// 执行Object的set方法为对象赋值
				objectMethod.invoke(instance, value);
			}
		}
		}catch(Exception e) {
			e.printStackTrace();
		}
		// 返回对象
		return instance;

	}

	private static String getNumberTypeName(String entityType) {
		switch (entityType) {
		case "java.lang.Double":
			return "double";
		case "java.lang.Float":
			return "float";
		case "java.lang.Integer":
			return "int";
		case "java.lang.Long":
			return "long";
		default :
			return entityType;
		}
	}

	/**
	 * @description 把表中的字段名称转化为实体类中的名称
	 * @author fengya
	 * @date 2017年6月26日 下午1:18:19
	 * @param rawColumnName
	 * @return
	 * @return String
	 */
	private static String getColumnName(String rawColumnName) {
		StringBuilder fieldName = new StringBuilder();
		if (rawColumnName.contains("_")) {
			String[] splitName = rawColumnName.split("_");
			for(String name : splitName) {
				if (!StringUtil.isNullOrEmpty(name)) {
					if (name.length() == 1) {
						fieldName.append(name.toLowerCase());
					} else {
						fieldName.append(name.substring(0, 1).toUpperCase()).append(name.substring(1).toLowerCase());
					}
				}
			}
		} else {
			fieldName.append(rawColumnName.substring(0, 1).toUpperCase() + rawColumnName.substring(1).toLowerCase());
		}
		return fieldName.toString();
	}

	/**
	 * 将java.sql.Types类型转化成相应的Java中对应的Class
	 *
	 * @param SQLType
	 *            java.sql.Types类型
	 * @return Class类型的实例例如 int.class
	 */
	private static Class<?> SQLTypeToClass(int SQLType) {
		switch (SQLType) {
		case java.sql.Types.ARRAY:
			return String.class;
		case java.sql.Types.TINYINT:
			return int.class;
		case java.sql.Types.BIGINT:
			return long.class;
		case java.sql.Types.BIT:
			return byte.class;
		case java.sql.Types.BOOLEAN:
			return boolean.class;
		case java.sql.Types.CHAR:
			return char.class;
		case java.sql.Types.DOUBLE:
			return Double.class;
		case java.sql.Types.DECIMAL:
			return Double.class;
		case java.sql.Types.FLOAT:
			return Double.class;
		case java.sql.Types.INTEGER:
			return Integer.class;
		case java.sql.Types.LONGNVARCHAR:
			return String.class;
		case java.sql.Types.LONGVARCHAR:
			return String.class;
		case java.sql.Types.NCHAR:
			return String.class;
		case java.sql.Types.NVARCHAR:
			return String.class;
		case java.sql.Types.VARCHAR:
			return String.class;
		// 通过rs.getDate()获取到的java.sql.Date能够直接将其赋值到java.util.Date会进行自动转换
		case java.sql.Types.DATE:
			return java.sql.Date.class;
		// oracle数据库的Number字段类型对应NUMERIC类型，
		// 但是在数据库中整数和浮点数都是Number，
		// 所以在应用中无法判断从数据库中取出的是整数还是浮点数.
		// 处理方法是：在应用中都使用浮点数
		case java.sql.Types.NUMERIC:
			return int.class;
		// 由于sun包中提供的CachedRowSetImpl类存在bug，当getTimestamp()字段时会出现类转换异常。
		// 解决方案有：
		// 1. 换类。oracle驱动包oracle.jdbc.rowset.OracleCachedRowSet类（大概是这个名字）可以正确执行。
		// 2. 换oracle驱动版本。10.0以上，这个我没有试过，有些人说可以。
		// 3. 如果使用sun提供的这个类，获取Timestamp时使用getObject()。然后再转换。
		// 因为考虑到该类的通用性，所以采用第三种解决方案。
		case java.sql.Types.TIMESTAMP:
//			return oracle.sql.TIMESTAMP.class;
			return java.util.Date.class;
		case java.sql.Types.LONGVARBINARY:
//			return oracle.sql.TIMESTAMP.class;
			return byte[].class;
		default:
			return String.class;
		}
	}

	/**
	 * @description 数据类型的转化
	 * @author fengya
	 * @date 2017年6月26日 下午1:19:13
	 * @param columnType
	 * @return
	 * @return String
	 */
	private static String columnTypeToGetter(String columnType) {
		switch(columnType){
		case "VARCHAR" : return "String";
		case "VARCHAR2" : return "String";
		case "TEXT" : return "String";
		case "CHAR" : return "String";
		case "CHARACTER" : return "String";
		case "NVARCHAR2" : return "String";
		case "NUMBER" : return "int";
		case "BIGINT" : return "long";
		case "TIMESTAMP" : return "object";
		case "DATE" : return "object";
		case "INT" : return "int";
		case "DATETIME" : return "object";
		case "DECIMAL" : return "double";
		case "DOUBLE" : return "double";
		default :
			return "String";
//			return columnType.toLowerCase();
		}
	}

}
